package com.aiwiown.snackmq.storage.index;

import com.aiwiown.snackmq.storage.config.StorageConfig;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Slf4j
@Getter // 修正：添加 Getter 注解以生成 getFileName() 等方法
public class IndexFile {
    private final String fileName;
    private final File file;
    private final FileChannel fileChannel;
    private final Map<String, IndexEntry> indexMap;
    private final Object writeLock = new Object();
    private final AtomicLong writePositionAtomic;

    // ... rest of the file remains the same ...
    // messageId(36) + fileIndex(4) + offset(8) + length(4) + timestamp(8) = 60 bytes
    private static final int MAX_MESSAGE_ID_LENGTH = 36; // UUID
    private static final int INDEX_ENTRY_SIZE = MAX_MESSAGE_ID_LENGTH + 4 + 8 + 4 + 8;

    public IndexFile(String fileName, StorageConfig config) throws IOException {
        this.fileName = fileName;
        this.file = new File(config.getBaseDir() + "/index", fileName);
        this.fileChannel = new RandomAccessFile(file, "rw").getChannel();
        this.indexMap = new ConcurrentHashMap<>();
        this.writePositionAtomic = new AtomicLong(0);

        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (!file.exists()) {
            file.createNewFile();
        }
        loadIndex();
    }

    private void loadIndex() throws IOException {
        synchronized (writeLock) {
            long fileSize = fileChannel.size();
            if (fileSize == 0) {
                log.info("Index file {} is new or empty.", file.getName());
                return;
            }

            ByteBuffer buffer = ByteBuffer.allocate((int) fileSize);
            fileChannel.read(buffer, 0);
            ((Buffer)buffer).flip();

            while (buffer.remaining() >= INDEX_ENTRY_SIZE) {
                byte[] messageIdBytes = new byte[MAX_MESSAGE_ID_LENGTH];
                buffer.get(messageIdBytes);
                String messageId = new String(messageIdBytes, StandardCharsets.UTF_8).trim();
                int fileIndex = buffer.getInt();
                long offset = buffer.getLong();
                int length = buffer.getInt();
                long timestamp = buffer.getLong();

                IndexEntry entry = new IndexEntry(messageId, fileIndex, offset, length, timestamp);
                indexMap.put(messageId, entry);
            }
            writePositionAtomic.set(fileChannel.size());
            log.info("Loaded {} indexes from file {}", indexMap.size(), file.getName());
        }
    }

    public void addIndex(IndexEntry entry) {
        synchronized (writeLock) {
            try {
                long currentPosition = writePositionAtomic.get();
                ByteBuffer buffer = ByteBuffer.allocate(INDEX_ENTRY_SIZE);

                byte[] messageIdBytes = entry.getMessageId().getBytes(StandardCharsets.UTF_8);
                if (messageIdBytes.length > MAX_MESSAGE_ID_LENGTH) {
                    throw new IllegalArgumentException("Message ID is too long: " + entry.getMessageId());
                }

                buffer.put(messageIdBytes);
                for (int i = 0; i < MAX_MESSAGE_ID_LENGTH - messageIdBytes.length; i++) {
                    buffer.put((byte) 0);
                }

                buffer.putInt(entry.getFileIndex());
                buffer.putLong(entry.getOffset());
                buffer.putInt(entry.getPosition());
                buffer.putLong(entry.getTimestamp());

                ((Buffer)buffer).flip();
                fileChannel.write(buffer, currentPosition);
                fileChannel.force(true);

                writePositionAtomic.addAndGet(INDEX_ENTRY_SIZE);
                indexMap.put(entry.getMessageId(), entry);

            } catch (Exception e) {
                log.error("Failed to add index for message: {}", entry.getMessageId(), e);
                throw new RuntimeException("Failed to add index", e);
            }
        }
    }

    public IndexEntry getIndex(String messageId) {
        if (messageId == null || messageId.isEmpty()) {
            log.warn("Message ID is null or empty");
            return null;
        }
        return indexMap.get(messageId);
    }

    public void removeIndex(String messageId) {
        indexMap.remove(messageId);
    }

    public Map<String, IndexEntry> getAllIndexes() {
        return indexMap;
    }

    public void close() {
        try {
            if (fileChannel != null && fileChannel.isOpen()) {
                fileChannel.close();
            }
        } catch (IOException e) {
            log.error("Error closing index file: {}", file.getName(), e);
        }
    }

    public long getFileSize() {
        try {
            return fileChannel.size();
        } catch (IOException e) {
            log.error("Failed to get file size for {}", fileName, e);
            return 0;
        }
    }

    public int getIndexCount() {
        return indexMap.size();
    }
}